package tcp.controller;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.TextField;
import org.apache.commons.codec.binary.Base64;
import tcp.encryption.DHCoder;

import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

public class ServerController implements Initializable {

    // 获取界面上的组件
    @FXML
    TextField my_ip;
    @FXML
    TextField publicKeyA;
    @FXML
    TextField publicKeyB;
    @FXML
    TextField commonKey;
    @FXML
    TextField openPort;
    @FXML
    TextField encryptTime;

    private List<Client> clients = new ArrayList<>();
    String data = null;

    //*******************************************8
    //甲方公钥
    private static byte[] publicKey1;
    //甲方私钥
    private static byte[] privateKey1;
    //甲方本地密钥
    private static byte[] key1;
    //乙方公钥
    private static byte[] publicKey2;
    //乙方私钥
    private static byte[] privateKey2;
    //乙方本地密钥
    private static byte[] key2;

    // 点击【开启服务器】按钮
    public void startServer() {
        // 获取IP地址并显示
        String ip = "";
        try {
            InetAddress addr = InetAddress.getLocalHost();
            ip = addr.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            ip = "未获取到IP地址!";
        }
        my_ip.setText(ip);

        Thread t = new Thread(new Runnable(){
            public void run(){
                new ServerController().init(openPort.getText());
            }});
        t.start();

//        publicKeyA.setText(Base64.encodeBase64String(publicKey1));
//        publicKeyB.setText(Base64.encodeBase64String(publicKey2));
    }

    // 点击【关闭服务器】按钮
    public void endServer() {
        System.exit(0); // 关闭系统
    }

    // 点击【生成密钥】按钮
    public void generateKey() {
        System.out.println("重新生成公钥!");
        try {
            initKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 设置值到界面上
        publicKeyA.setText(Base64.encodeBase64String(publicKey1));
        publicKeyB.setText(Base64.encodeBase64String(publicKey2));
    }

    // 点击【生成共同密钥】按钮
    public void generateCommonKey() {
        System.out.println("生成密钥");
        commonKey.setText(Base64.encodeBase64String(key2));
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        openPort.setText("8888");
    }

    /**
     * 初始化密钥
     * @throws Exception
     */
    public static final void initKey() throws Exception{
        // 生成甲方密钥对
        Map<String, Object> keyMap1 = DHCoder.initKey();
        publicKey1 = DHCoder.getPublicKey(keyMap1);
        privateKey1 = DHCoder.getPrivateKey(keyMap1);
        System.out.println("甲方公钥:\n" + Base64.encodeBase64String(publicKey1));
        System.out.println("甲方私钥:\n" + Base64.encodeBase64String(privateKey1));
        // 由甲方公钥产生本地密钥对
        Map<String, Object> keyMap2 = DHCoder.initKey(publicKey1);
        publicKey2 = DHCoder.getPublicKey(keyMap2);
        privateKey2 = DHCoder.getPrivateKey(keyMap2);
        System.out.println("乙方公钥:\n" + Base64.encodeBase64String(publicKey2));
        System.out.println("乙方私钥:\n" + Base64.encodeBase64String(privateKey2));
        key1 = DHCoder.getSecretKey(publicKey2, privateKey1);
        System.out.println("甲方本地密钥:\n" + Base64.encodeBase64String(key1));
        key2 = DHCoder.getSecretKey(publicKey1, privateKey2);
        System.out.println("乙方本地密钥:\n" + Base64.encodeBase64String(key2));
    }

    //*******************************************

    public void init(String port) {
        System.out.println("服务器已开启");
        // 初始化服务器配置
        ServerSocket ss = null;
        Socket socket = null;
        try {
            // 1开启服务器端socket，即serversocket，指定绑定的端口
            // 指定端口 使用serversocket创建服务器
            ss = new ServerSocket(Integer.valueOf(port));
        } catch (BindException e) {
            System.out.println("端口已被占用");
            e.printStackTrace();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // 连接客户端
        try {
            Client client = null;
            //2接收客户端连接请求 调用accept（）等待客户端连接
            //阻塞式等待连接 accept
            while (true) {
                try {
                } catch (Exception e) {
                    e.printStackTrace();
                }
                socket = ss.accept();
                System.out.println("客户驾到"); //开启线程处理客户端通信
                System.out.println(socket.toString());
                client = new Client(socket);
                clients.add(client);//使用容器管理所有成员客户端
                new Thread(client).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private class Client implements Runnable {
        private Socket socket = null;
        //3连接后获取输入流，读取客户端信息
        InputStream in = null;
        DataInputStream din = null;
        OutputStream out = null;
        DataOutputStream dos = null;
        boolean flag = true;
        //发送消息的函数
        public Client(Socket socket) {
            this.socket = socket;
            try {
                in = socket.getInputStream();//获取输入流
                din = new DataInputStream(in);
            } catch (IOException e) {
                System.out.println("接受消息失败");
                e.printStackTrace();
            }

        }

        public void run() {
            String message;
            try {
                while (flag) {
                    message = din.readUTF();//循环读取客户端的信息
                    // System.out.println("客户说：" + message);
                    try {
                        forwordToAllClients(message);//转发给所有的客户端
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (SocketException e) {
                flag = false;
                System.out.println("客户下线");
                clients.remove(this);
                // e.printStackTrace();
            } catch (EOFException e) {
                flag = false;
                System.out.println("客户下线");
                clients.remove(this);
                // e.printStackTrace();
            } catch (IOException e) {
                flag = false;
                System.out.println("接受消息失败");
                clients.remove(this);
                e.printStackTrace();
            }

            if (din != null) {
                try {
                    din.close();
                } catch (IOException e) {
                    System.out.println("din关闭失败");
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    System.out.println("din关闭失败");
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    System.out.println("din关闭失败");
                    e.printStackTrace();
                }
            }

        }

        /**
         *
         * @param message
         * @throws IOException
         */
        // 调用的方法，这个方法怎么运行的就是说将你所有注册到这个服务端的客户端，意思就是每次启动一
        // 个客户端都会将这个客户端的信息注册到这个服务器里来，然后这里面会进行一个循环，循环就是把
        // 这个全部信息进行转发forwordToClient(c,message);这里面参数是c(客户端)，message是信息。
        private void forwordToAllClients(String message) throws Exception {
            System.out.println("forwordToAllClients " + message);
            if (message.contains("jiemi")) {
                for (Client c : clients) {
                    if (c == this) {
                        InetAddress inetAddress = c.socket.getInetAddress();
                        System.out.println(inetAddress);
                        out = c.socket.getOutputStream();
                        dos = new DataOutputStream(out);
                        forwordToClient(c,message); // 这里面参数是c(客户端)，message是信息。
                    }
                }
            }
            else if (message.contains("jiami")){
                for (Client c : clients) {
                    if (c != this) {
                        out = c.socket.getOutputStream();
                        dos = new DataOutputStream(out);
                        forwordToClient(c, message);
                    }
                }
            }

            else {
                for (Client c : clients) {
                    if (c != this) {
                        out = c.socket.getOutputStream();
                        dos = new DataOutputStream(out);
                        forwordToClient(c, message);
                    }
                }
            }

        }

        // 具体转发工作的代码
        private void forwordToClient(Client c, String message) throws Exception {
            byte[] encode1=null;
            //判断信息是否加密
            // jiamiC://desktop//a.txt
            if (message.contains("jiami")) {
                String runtime = null;
                long startTime = System.currentTimeMillis();    //获取开始时间

                String username = message.substring(0,9);
                System.out.println("username" + username);
                message = message.substring(9);
                System.out.println("message" +message);
                String jiami = message.replace("jiami", "");
                System.out.println("---使用甲方本地密钥对数据进行加密---");
                //使用甲方本地密钥对数据加密
                encode1  = DHCoder.encrypt(jiami.getBytes(), key1); // 最重要
                System.out.println("加密:\n" + Base64.encodeBase64String(encode1));
                data= Base64.encodeBase64String(encode1);
                System.out.println(encode1);
                String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
                message = "jiami" + time + "-IP地址：-"+c.socket.getInetAddress()+"--"+username+"加密后的内容为："+Base64.encodeBase64String(encode1);
                System.out.println("---使用乙方本地密钥对数据库进行解密---");

                long endTime = System.currentTimeMillis();    //获取结束时间
                System.out.println("本次加密时间：" + (endTime - startTime) + "ms");    //输出程序运行时间
//                encryptTime.setText( (endTime - startTime) + "ms");
            }
            //判断信息是否解密
            else if (message.contains("jiemi")) {
                if (message.substring(9).equals("jiemi")) {
                    byte[] bytes = Base64.decodeBase64(data);
                    byte[] decode1 = DHCoder.decrypt(bytes, key2);
                    String output1 = new String(decode1);
                    System.out.println("解密:\n" + output1);
                    message = "解密后的内容为：" + output1;
                } else {
                    String jiemi = message.replace("jiemi", "");
                    System.out.println(jiemi);
                    byte[] bytes = Base64.decodeBase64(jiemi);
                    System.out.println(bytes);
                    System.out.println("---使用乙方本地密钥对数据库进行解密---");
                    byte[] decode1 = DHCoder.decrypt(bytes, key2);
                    String output1 = new String(decode1);
                    System.out.println("解密:\n" + output1);
                    message = "解密后的内容为：" + output1;
                }
            } else {
                String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
                message = time+"-IP地址：-"+c.socket.getInetAddress()+"--"+message;
            }
            dos.writeUTF(message); // 发给客户端
            dos.flush();
            System.out.println("转发成功！");
        }

    }

    // 弹框方法封装
    public void alert(String title, String headerText, String contentText) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle(title);
        alert.setHeaderText(headerText);
        alert.setContentText(contentText);
        alert.showAndWait();
    }

}
